<html>
<head>
    <style>
        * {
            font-family: 'Consolas', 'Ubuntu Mono', 'Monaco', 'Courier New', Courier, sans-serif;
        }
        body {
            margin: 0;
            padding: 0;
        }
        #editor-container {
            padding: 0 10px;
            width: 33%;
            display: flex;
            flex-direction: column;
            max-height: 100vh;
        }
        textarea {
            width: 100%;
            flex-grow: 1;
            min-height: 300px;
        }
        #debug-container {
            width: 66%;
            overflow-x: hidden;
            background: #eaeaea;
            font-size: 0.7rem;
            padding: 10px;
            margin: 0;
        }
        #debug-state,
        #debug-txs,
        #debug-network {
            min-height: 100vh;
        }
        .hidden {
            display: none;
        }
    </style>
</head>
<body>
    <div style='display: flex'>
        <div id='editor-container'>
            <h1>collaborative editor</h1>

            <textarea id='editor'></textarea>
        </div>
        <div id='debug-container'>
            <button id='btn-view-debug-state' disabled>State</button>
            <button id='btn-view-debug-txs'>Transactions</button>
            <button id='btn-view-debug-network'>Network</button>
            <div id='tab-state'>
                <code>
                    <pre id='debug-state'>
                    </pre>
                </code>
            </div>
            <div id='tab-txs' class='hidden'>
                <code>
                    <pre id='debug-txs'>
                    </pre>
                </code>
            </div>
            <div id='tab-network' class='hidden'>
                <code>
                    <pre id='debug-network'>
                    </pre>
                </code>
            </div>
        </div>
    </div>
</body>

<script src="https://unpkg.com/peerjs@1.0.0/dist/peerjs.min.js"></script>
<script src='/redwood.js'></script>
<script>
    //
    // Redwood/sync9 setup
    //
    var identity = Redwood.identity.random()
    var s9 = Redwood.sync9.create()

    var editor = document.querySelector('#editor')
    var queue = Redwood.utils.createTxQueue(
        (from, vid, parents, patches) => Redwood.sync9.resolve_state(s9, from, vid, parents, patches),
        async ({ tx, state }) => {
            // Update the debug state UI
            currentState = state
            if (knownTxs[tx.id]) {
                return
            }

            txs.push(tx)
            knownTxs[tx.id] = true
            refreshDebugUI()

            // Update the editor UI
            if (state && state.text) {
                let n = 0
                for (let patch of tx.patches) {
                    let parsed = Redwood.sync9.parse_change(patch)
                    if (parsed.range && parsed.range[0] <= editor.selectionStart) {
                        n += parsed.val.length - (parsed.range[1] - parsed.range[0])
                    }
                }
                let selectionStart = editor.selectionStart + n
                let selectionEnd = editor.selectionEnd + n

                editor.value = state.text.value
                editor.selectionStart = selectionEnd
                editor.selectionEnd = selectionEnd
            }
        }
    )

    var braidClient = Redwood.createPeer({
        identity: identity,
        httpHost: '',
        webrtc: true,
        onFoundPeersCallback: (peers) => {
            knownPeers = peers
            refreshDebugUI()
        }
    })

    braidClient.authorize().then(() => {
        braidClient.subscribe({
            stateURI: 'docs.redwood.dev/document-3192',
            keypath: '/',
            txs: true,
            fromTxID: Redwood.utils.genesisTxID,
            callback: queue.defaultTxHandler,
        })
    })


    //
    // Editor UI
    //
    editor.addEventListener('keydown', (event) => {
        if (['UP', 'DOWN', 'LEFT', 'RIGHT'].indexOf(keyboardNameMap[event.keyCode]) > -1) {
            return
        }

        event.stopPropagation()
        event.preventDefault()
        var char = getPressedChar(event)
        if (char === '') {
            return
        }
        var tx = {
            id: Redwood.utils.randomID(),
            parents: Object.keys(s9.leaves),
            stateURI: 'docs.redwood.dev/document-3192',
            patches: [
                '.text.value[' + editor.selectionStart + ':' + editor.selectionEnd + '] = ' + JSON.stringify(char),
            ],
        }

        queue.addTx(tx)

        try {
            braidClient.put(tx)
        } catch (err) {
            console.error('error sending to peer ~>', err)
        }
    })

    //
    // Debug UI
    //
    var txs = []
    var knownTxs = {}
    var currentState = {}
    var knownPeers = {}
    var currentDebugTab = 'state'
    var debugTabButtonState = document.getElementById('btn-view-debug-state')
    var debugTabButtonTxs = document.getElementById('btn-view-debug-txs')
    var debugTabButtonNetwork = document.getElementById('btn-view-debug-network')
    var debugStateElem = document.getElementById('debug-state')
    var debugTxsElem = document.getElementById('debug-txs')
    var debugNetworkElem = document.getElementById('debug-network')
    var debugStateTab = document.getElementById('tab-state')
    var debugTxsTab = document.getElementById('tab-txs')
    var debugNetworkTab = document.getElementById('tab-network')
    function refreshDebugUI() {
        debugStateElem.innerHTML = JSON.stringify(currentState, null, 4)
        debugTxsElem.innerHTML = JSON.stringify(txs, null, 4)
        debugNetworkElem.innerHTML = JSON.stringify(knownPeers, null, 4)
    }

    function changeDebugUITab() {
        if (currentDebugTab === 'state') {
            debugStateTab.classList.remove('hidden')
            debugTxsTab.classList.add('hidden')
            debugNetworkTab.classList.add('hidden')
            debugTabButtonState.disabled = true
            debugTabButtonTxs.disabled = false
            debugTabButtonNetwork.disabled = false
        } else if (currentDebugTab === 'txs') {
            debugStateTab.classList.add('hidden')
            debugTxsTab.classList.remove('hidden')
            debugNetworkTab.classList.add('hidden')
            debugTabButtonState.disabled = false
            debugTabButtonTxs.disabled = true
            debugTabButtonNetwork.disabled = false
        } else if (currentDebugTab === 'network') {
            debugStateTab.classList.add('hidden')
            debugTxsTab.classList.add('hidden')
            debugNetworkTab.classList.remove('hidden')
            debugTabButtonState.disabled = false
            debugTabButtonTxs.disabled = false
            debugTabButtonNetwork.disabled = true
        }
    }
    debugTabButtonState.addEventListener('click', () => {
        currentDebugTab = 'state'
        changeDebugUITab()
    })
    debugTabButtonTxs.addEventListener('click', () => {
        currentDebugTab = 'txs'
        refreshDebugUI()
        changeDebugUITab()
    })
    debugTabButtonNetwork.addEventListener('click', () => {
        currentDebugTab = 'network'
        changeDebugUITab()
    })

    function getPressedChar(event) {
        var iCol = 0
        if (event.shiftKey) {
            iCol = 1
        }
        return keyboardCharMap[event.keyCode][iCol]
    }

// names of known key codes (0-255)
var keyboardNameMap = [
  "", // [0]
  "", // [1]
  "", // [2]
  "CANCEL", // [3]
  "", // [4]
  "", // [5]
  "HELP", // [6]
  "", // [7]
  "BACK_SPACE", // [8]
  "TAB", // [9]
  "", // [10]
  "", // [11]
  "CLEAR", // [12]
  "ENTER", // [13]
  "ENTER_SPECIAL", // [14]
  "", // [15]
  "SHIFT", // [16]
  "CONTROL", // [17]
  "ALT", // [18]
  "PAUSE", // [19]
  "CAPS_LOCK", // [20]
  "KANA", // [21]
  "EISU", // [22]
  "JUNJA", // [23]
  "FINAL", // [24]
  "HANJA", // [25]
  "", // [26]
  "ESCAPE", // [27]
  "CONVERT", // [28]
  "NONCONVERT", // [29]
  "ACCEPT", // [30]
  "MODECHANGE", // [31]
  "SPACE", // [32]
  "PAGE_UP", // [33]
  "PAGE_DOWN", // [34]
  "END", // [35]
  "HOME", // [36]
  "LEFT", // [37]
  "UP", // [38]
  "RIGHT", // [39]
  "DOWN", // [40]
  "SELECT", // [41]
  "PRINT", // [42]
  "EXECUTE", // [43]
  "PRINTSCREEN", // [44]
  "INSERT", // [45]
  "DELETE", // [46]
  "", // [47]
  "0", // [48]
  "1", // [49]
  "2", // [50]
  "3", // [51]
  "4", // [52]
  "5", // [53]
  "6", // [54]
  "7", // [55]
  "8", // [56]
  "9", // [57]
  "COLON", // [58]
  "SEMICOLON", // [59]
  "LESS_THAN", // [60]
  "EQUALS", // [61]
  "GREATER_THAN", // [62]
  "QUESTION_MARK", // [63]
  "AT", // [64]
  "A", // [65]
  "B", // [66]
  "C", // [67]
  "D", // [68]
  "E", // [69]
  "F", // [70]
  "G", // [71]
  "H", // [72]
  "I", // [73]
  "J", // [74]
  "K", // [75]
  "L", // [76]
  "M", // [77]
  "N", // [78]
  "O", // [79]
  "P", // [80]
  "Q", // [81]
  "R", // [82]
  "S", // [83]
  "T", // [84]
  "U", // [85]
  "V", // [86]
  "W", // [87]
  "X", // [88]
  "Y", // [89]
  "Z", // [90]
  "OS_KEY", // [91] Windows Key (Windows) or Command Key (Mac)
  "", // [92]
  "CONTEXT_MENU", // [93]
  "", // [94]
  "SLEEP", // [95]
  "NUMPAD0", // [96]
  "NUMPAD1", // [97]
  "NUMPAD2", // [98]
  "NUMPAD3", // [99]
  "NUMPAD4", // [100]
  "NUMPAD5", // [101]
  "NUMPAD6", // [102]
  "NUMPAD7", // [103]
  "NUMPAD8", // [104]
  "NUMPAD9", // [105]
  "MULTIPLY", // [106]
  "ADD", // [107]
  "SEPARATOR", // [108]
  "SUBTRACT", // [109]
  "DECIMAL", // [110]
  "DIVIDE", // [111]
  "F1", // [112]
  "F2", // [113]
  "F3", // [114]
  "F4", // [115]
  "F5", // [116]
  "F6", // [117]
  "F7", // [118]
  "F8", // [119]
  "F9", // [120]
  "F10", // [121]
  "F11", // [122]
  "F12", // [123]
  "F13", // [124]
  "F14", // [125]
  "F15", // [126]
  "F16", // [127]
  "F17", // [128]
  "F18", // [129]
  "F19", // [130]
  "F20", // [131]
  "F21", // [132]
  "F22", // [133]
  "F23", // [134]
  "F24", // [135]
  "", // [136]
  "", // [137]
  "", // [138]
  "", // [139]
  "", // [140]
  "", // [141]
  "", // [142]
  "", // [143]
  "NUM_LOCK", // [144]
  "SCROLL_LOCK", // [145]
  "WIN_OEM_FJ_JISHO", // [146]
  "WIN_OEM_FJ_MASSHOU", // [147]
  "WIN_OEM_FJ_TOUROKU", // [148]
  "WIN_OEM_FJ_LOYA", // [149]
  "WIN_OEM_FJ_ROYA", // [150]
  "", // [151]
  "", // [152]
  "", // [153]
  "", // [154]
  "", // [155]
  "", // [156]
  "", // [157]
  "", // [158]
  "", // [159]
  "CIRCUMFLEX", // [160]
  "EXCLAMATION", // [161]
  "DOUBLE_QUOTE", // [162]
  "HASH", // [163]
  "DOLLAR", // [164]
  "PERCENT", // [165]
  "AMPERSAND", // [166]
  "UNDERSCORE", // [167]
  "OPEN_PAREN", // [168]
  "CLOSE_PAREN", // [169]
  "ASTERISK", // [170]
  "PLUS", // [171]
  "PIPE", // [172]
  "HYPHEN_MINUS", // [173]
  "OPEN_CURLY_BRACKET", // [174]
  "CLOSE_CURLY_BRACKET", // [175]
  "TILDE", // [176]
  "", // [177]
  "", // [178]
  "", // [179]
  "", // [180]
  "VOLUME_MUTE", // [181]
  "VOLUME_DOWN", // [182]
  "VOLUME_UP", // [183]
  "", // [184]
  "", // [185]
  "SEMICOLON", // [186]
  "EQUALS", // [187]
  "COMMA", // [188]
  "MINUS", // [189]
  "PERIOD", // [190]
  "SLASH", // [191]
  "BACK_QUOTE", // [192]
  "", // [193]
  "", // [194]
  "", // [195]
  "", // [196]
  "", // [197]
  "", // [198]
  "", // [199]
  "", // [200]
  "", // [201]
  "", // [202]
  "", // [203]
  "", // [204]
  "", // [205]
  "", // [206]
  "", // [207]
  "", // [208]
  "", // [209]
  "", // [210]
  "", // [211]
  "", // [212]
  "", // [213]
  "", // [214]
  "", // [215]
  "", // [216]
  "", // [217]
  "", // [218]
  "OPEN_BRACKET", // [219]
  "BACK_SLASH", // [220]
  "CLOSE_BRACKET", // [221]
  "QUOTE", // [222]
  "", // [223]
  "META", // [224]
  "ALTGR", // [225]
  "", // [226]
  "WIN_ICO_HELP", // [227]
  "WIN_ICO_00", // [228]
  "", // [229]
  "WIN_ICO_CLEAR", // [230]
  "", // [231]
  "", // [232]
  "WIN_OEM_RESET", // [233]
  "WIN_OEM_JUMP", // [234]
  "WIN_OEM_PA1", // [235]
  "WIN_OEM_PA2", // [236]
  "WIN_OEM_PA3", // [237]
  "WIN_OEM_WSCTRL", // [238]
  "WIN_OEM_CUSEL", // [239]
  "WIN_OEM_ATTN", // [240]
  "WIN_OEM_FINISH", // [241]
  "WIN_OEM_COPY", // [242]
  "WIN_OEM_AUTO", // [243]
  "WIN_OEM_ENLW", // [244]
  "WIN_OEM_BACKTAB", // [245]
  "ATTN", // [246]
  "CRSEL", // [247]
  "EXSEL", // [248]
  "EREOF", // [249]
  "PLAY", // [250]
  "ZOOM", // [251]
  "", // [252]
  "PA1", // [253]
  "WIN_OEM_CLEAR", // [254]
  "" // [255]
];

// This has the UnShifted and Shifted characters that each key maps to
// Ones that are to be ignored for character input are empty.
var keyboardCharMap = [
  ["",""], // [0]
  ["",""], // [1]
  ["",""], // [2]
  ["",""], // [3]
  ["",""], // [4]
  ["",""], // [5]
  ["",""], // [6]
  ["",""], // [7]
  ["",""], // [8]
  ["",""], // [9]
  ["", ""], // [10]
  ["",""], // [11]
  ["",""], // [12]
  ["\r", "\r"], // [13]	- MOST control characters are ignored.  This one (Carriage Return, or "Enter") is significant!
  ["",""], // [14]
  ["",""], // [15]
  ["",""], // [16]
  ["",""], // [17]
  ["",""], // [18]
  ["",""], // [19]
  ["",""], // [20]
  ["",""], // [21]
  ["",""], // [22]
  ["",""], // [23]
  ["",""], // [24]
  ["",""], // [25]
  ["",""], // [26]
  ["",""], // [27]
  ["",""], // [28]
  ["",""], // [29]
  ["",""], // [30]
  ["",""], // [31]
  [" "," "], // [32]	// SPACE!  Don't "clean it up" and remove the space!
  ["",""], // [33]
  ["",""], // [34]
  ["",""], // [35]
  ["",""], // [36]
  ["",""], // [37]
  ["",""], // [38]
  ["",""], // [39]
  ["",""], // [40]
  ["",""], // [41]
  ["",""], // [42]
  ["",""], // [43]
  ["",""], // [44]
  ["",""], // [45]
  ["",""], // [46]
  ["",""], // [47]
  ["0",")"], // [48]
  ["1","!"], // [49]
  ["2","@"], // [50]
  ["3","#"], // [51]
  ["4","$"], // [52]
  ["5","%"], // [53]
  ["6","^"], // [54]
  ["7","&"], // [55]
  ["8","*"], // [56]
  ["9","("], // [57]
  ["",""], // [58]
  [";",":"], // [59]
  ["<",""], // [60]
  ["=",""], // [61]
  [">",""], // [62]
  ["?",""], // [63]	shifted;  else "/"
  ["",""], // [64]
  ["a","A"], // [65]
  ["b","B"], // [66]
  ["c","C"], // [67]
  ["d","D"], // [68]
  ["e","E"], // [69]
  ["f","F"], // [70]
  ["g","G"], // [71]
  ["h","H"], // [72]
  ["i","I"], // [73]
  ["j","J"], // [74]
  ["k","K"], // [75]
  ["l","L"], // [76]
  ["m","M"], // [77]
  ["n","N"], // [78]
  ["o","O"], // [79]
  ["p","P"], // [80]
  ["q","Q"], // [81]
  ["r","R"], // [82]
  ["s","S"], // [83]
  ["t","T"], // [84]
  ["u","U"], // [85]
  ["v","V"], // [86]
  ["w","W"], // [87]
  ["x","X"], // [88]
  ["y","Y"], // [89]
  ["z","Z"], // [90]
  ["",""], // [91] Windows Key (Windows) or Command Key (Mac)
  ["",""], // [92]
  ["",""], // [93]
  ["",""], // [94]
  ["",""], // [95]
  // Number Keypad Entries...
  ["0",""], // [96]
  ["1",""], // [97]
  ["2",""], // [98]
  ["3",""], // [99]
  ["4",""], // [100]
  ["5",""], // [101]
  ["6",""], // [102]
  ["7",""], // [103]
  ["8",""], // [104]
  ["9",""], // [105]
  ["*",""], // [106]
  ["+",""], // [107]
  ["",""], // [108]
  ["-",""], // [109]
  [".",""], // [110]
  ["/",""], // [111]

  ["",""], // [112]
  ["",""], // [113]
  ["",""], // [114]
  ["",""], // [115]
  ["",""], // [116]
  ["",""], // [117]
  ["",""], // [118]
  ["",""], // [119]
  ["",""], // [120]
  ["",""], // [121]
  ["",""], // [122]
  ["",""], // [123]
  ["",""], // [124]
  ["",""], // [125]
  ["",""], // [126]
  ["",""], // [127]
  ["",""], // [128]
  ["",""], // [129]
  ["",""], // [130]
  ["",""], // [131]
  ["",""], // [132]
  ["",""], // [133]
  ["",""], // [134]
  ["",""], // [135]
  ["",""], // [136]
  ["",""], // [137]
  ["",""], // [138]
  ["",""], // [139]
  ["",""], // [140]
  ["",""], // [141]
  ["",""], // [142]
  ["",""], // [143]
  ["",""], // [144]
  ["",""], // [145]
  ["",""], // [146]
  ["",""], // [147]
  ["",""], // [148]
  ["",""], // [149]
  ["",""], // [150]
  ["",""], // [151]
  ["",""], // [152]
  ["",""], // [153]
  ["",""], // [154]
  ["",""], // [155]
  ["",""], // [156]
  ["",""], // [157]
  ["",""], // [158]
  ["",""], // [159]
  ["",""], // [160]
  ["",""], // [161]
  ["",""], // [162]
  ["",""], // [163]
  ["",""], // [164]
  ["",""], // [165]
  ["",""], // [166]
  ["",""], // [167]
  ["",""], // [168]
  ["",""], // [169]
  ["",""], // [170]
  ["",""], // [171]
  ["",""], // [172]
  ["",""], // [173]
  ["",""], // [174]
  ["",""], // [175]
  ["",""], // [176]
  ["",""], // [177]
  ["",""], // [178]
  ["",""], // [179]
  ["",""], // [180]
  ["",""], // [181]
  ["",""], // [182]
  ["",""], // [183]
  ["",""], // [184]
  ["",""], // [185]
  ["",""], // [186]
  ["=","+"], // [187]
  [",","<"], // [188]
  ["-","_"], // [189]
  [".",">"], // [190]
  ["/","?"], // [191]
  ["`","~"], // [192]
  ["",""], // [193]
  ["",""], // [194]
  ["",""], // [195]
  ["",""], // [196]
  ["",""], // [197]
  ["",""], // [198]
  ["",""], // [199]
  ["",""], // [200]
  ["",""], // [201]
  ["",""], // [202]
  ["",""], // [203]
  ["",""], // [204]
  ["",""], // [205]
  ["",""], // [206]
  ["",""], // [207]
  ["",""], // [208]
  ["",""], // [209]
  ["",""], // [210]
  ["",""], // [211]
  ["",""], // [212]
  ["",""], // [213]
  ["",""], // [214]
  ["",""], // [215]
  ["",""], // [216]
  ["",""], // [217]
  ["",""], // [218]
  ["[","{"], // [219]
  ["\\","|"], // [220]
  ["]","}"], // [221]
  ["'","\""], // [222]
  ["",""], // [223]
  ["",""], // [224]
  ["",""], // [225]
  ["",""], // [226]
  ["",""], // [227]
  ["",""], // [228]
  ["",""], // [229]
  ["",""], // [230]
  ["",""], // [231]
  ["",""], // [232]
  ["",""], // [233]
  ["",""], // [234]
  ["",""], // [235]
  ["",""], // [236]
  ["",""], // [237]
  ["",""], // [238]
  ["",""], // [239]
  ["",""], // [240]
  ["",""], // [241]
  ["",""], // [242]
  ["",""], // [243]
  ["",""], // [244]
  ["",""], // [245]
  ["",""], // [246]
  ["",""], // [247]
  ["",""], // [248]
  ["",""], // [249]
  ["",""], // [250]
  ["",""], // [251]
  ["",""], // [252]
  ["",""], // [253]
  ["",""], // [254]
  ["",""] // [255]
];

</script>
</html>
